/*
 * GraphExplorerMain.java
 * Created on May 14, 2010, 10:10:30 AM
 */

package graphexplorer;

import graphexplorer.controller.GraphControllerMaster;
import graphexplorer.controller.GraphController;
import graphexplorer.actions.ExplorerLayoutActions;
import graphexplorer.actions.ExplorerIOActions;
import data.propertysheet.PropertySheet;
import data.propertysheet.editor.EditorRegistration;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Point2D;
import java.util.Map;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.TableModelEvent;
import org.bm.blaise.scio.graph.Graph;
import org.bm.blaise.scio.graph.LongitudinalGraph;
import org.bm.blaise.scio.graph.layout.IterativeGraphLayout;
import org.bm.blaise.scio.graph.layout.StaticGraphLayout;
import org.bm.blaise.specto.plane.graph.PlaneGraph;
import org.bm.blaise.specto.plane.graph.PlaneGraphBean;
import stormtimer.BetterTimer;
import visometry.PlotComponent;

/**
 *
 * @author Elisha Peterson
 */
public class GraphExplorerLite extends javax.swing.JFrame
        implements ChangeListener, GraphExplorerInterface {

    /** Controller */
    GraphControllerMaster controller;

    /** Tracks the active graph in the explorer */
    PlaneGraph activeGraph;

    /** Creates new form GraphExplorerMain */
    public GraphExplorerLite() {
        try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception e) { }
        EditorRegistration.registerEditors();
        initActions();
        initComponents();
        mainPlot.add(activeGraph = new PlaneGraph());
        activeGraph.addChangeListener(this);
        initPropertyPanel();
    }

    /** File/IO actions */
    ExplorerIOActions actions_io;
    /** Layout actions */
    ExplorerLayoutActions actions_layout;

    private void initActions() {
        controller = new GraphControllerMaster();
        actions_io = new ExplorerIOActions(controller);
        actions_layout = new ExplorerLayoutActions(null);
    }

    /** Updates the property panel with active graph and energy layout */
    private void initPropertyPanel() {
        PlaneGraphBean pgb = new PlaneGraphBean(activeGraph);
        propertyRP.add(new PropertySheet(pgb.nodeBean()), "Node Settings");
        propertyRP.add(new PropertySheet(pgb.edgeBean()), "Edge Settings");
        propertyRP.add(new PropertySheet(pgb.labelBean()), "Label Settings");
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        mainTM = new graphexplorer.views.GraphTableModel();
        buttonGroup1 = new javax.swing.ButtonGroup();
        layoutPS = new data.propertysheet.PropertySheet();
        toolbar = new javax.swing.JToolBar();
        loadB = new javax.swing.JButton();
        saveB = new javax.swing.JButton();
        jSeparator2 = new javax.swing.JToolBar.Separator();
        jLabel1 = new javax.swing.JLabel();
        circleLayoutB = new javax.swing.JButton();
        randomLayoutB = new javax.swing.JButton();
        energyLayoutB = new javax.swing.JToggleButton();
        jSeparator1 = new javax.swing.JToolBar.Separator();
        mainSP = new javax.swing.JSplitPane();
        propertySP = new javax.swing.JScrollPane();
        propertyRP = new gui.RollupPanel();
        mainPlot = new visometry.plane.PlanePlotComponent();

        mainTM.addTableModelListener(new javax.swing.event.TableModelListener() {
            public void tableChanged(javax.swing.event.TableModelEvent evt) {
                mainTMTableChanged(evt);
            }
        });

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("Graph Explorer");

        toolbar.setRollover(true);

        loadB.setAction(actions_io.LOAD_ACTION);
        loadB.setText("Load");
        loadB.setFocusable(false);
        loadB.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        loadB.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        toolbar.add(loadB);

        saveB.setAction(actions_io.SAVE_ACTION);
        saveB.setText("Save");
        saveB.setFocusable(false);
        saveB.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        saveB.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        toolbar.add(saveB);
        toolbar.add(jSeparator2);

        jLabel1.setFont(new java.awt.Font("Tahoma", 3, 13));
        jLabel1.setText("Layout:");
        toolbar.add(jLabel1);

        circleLayoutB.setAction(actions_layout.LAYOUT_CIRCULAR);
        circleLayoutB.setText("Circle");
        circleLayoutB.setFocusable(false);
        circleLayoutB.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        circleLayoutB.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        toolbar.add(circleLayoutB);

        randomLayoutB.setAction(actions_layout.LAYOUT_RANDOM);
        randomLayoutB.setText("Random");
        randomLayoutB.setFocusable(false);
        randomLayoutB.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        randomLayoutB.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        toolbar.add(randomLayoutB);

        energyLayoutB.setText("Energy");
        energyLayoutB.setFocusable(false);
        energyLayoutB.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        energyLayoutB.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        energyLayoutB.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                energyLayoutBActionPerformed(evt);
            }
        });
        toolbar.add(energyLayoutB);
        toolbar.add(jSeparator1);

        getContentPane().add(toolbar, java.awt.BorderLayout.NORTH);

        mainSP.setDividerSize(8);
        mainSP.setOneTouchExpandable(true);

        propertySP.setPreferredSize(new java.awt.Dimension(200, 400));
        propertySP.setViewportView(propertyRP);

        mainSP.setLeftComponent(propertySP);
        mainSP.setRightComponent(mainPlot);

        getContentPane().add(mainSP, java.awt.BorderLayout.CENTER);

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void mainTMTableChanged(javax.swing.event.TableModelEvent evt) {//GEN-FIRST:event_mainTMTableChanged
        // only look at individual cell updates
        if (evt.getType() == TableModelEvent.UPDATE && evt.getColumn() != TableModelEvent.ALL_COLUMNS && evt.getFirstRow() != TableModelEvent.HEADER_ROW)
            activeGraph.setLabel(evt.getFirstRow(), (String) mainTM.getValueAt(evt.getFirstRow(), evt.getColumn()));
    }//GEN-LAST:event_mainTMTableChanged

    private void energyLayoutBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_energyLayoutBActionPerformed
        if (energyLayoutB.isSelected())
            actions_layout.LAYOUT_ENERGY_START.actionPerformed(evt);
        else
            actions_layout.LAYOUT_STOP.actionPerformed(evt);
    }//GEN-LAST:event_energyLayoutBActionPerformed

    /**
    * @param args the command line arguments
    */
    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
        public void run() {
                new GraphExplorerLite().setVisible(true);
            }
        });
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.ButtonGroup buttonGroup1;
    private javax.swing.JButton circleLayoutB;
    private javax.swing.JToggleButton energyLayoutB;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JToolBar.Separator jSeparator1;
    private javax.swing.JToolBar.Separator jSeparator2;
    private data.propertysheet.PropertySheet layoutPS;
    private javax.swing.JButton loadB;
    private visometry.plane.PlanePlotComponent mainPlot;
    private javax.swing.JSplitPane mainSP;
    private graphexplorer.views.GraphTableModel mainTM;
    private gui.RollupPanel propertyRP;
    private javax.swing.JScrollPane propertySP;
    private javax.swing.JButton randomLayoutB;
    private javax.swing.JButton saveB;
    private javax.swing.JToolBar toolbar;
    // End of variables declaration//GEN-END:variables

    //
    // UTILITIES
    //

    public void output(String output) {
        System.out.println(output);
    }

    //
    // PROPERTY GETTERS
    //

    public Component dialogComponent() { return this; }
    public PlotComponent activePlotComponent() { return mainPlot; }
    public Graph activeGraph() {
        return activeGraph == null ? null : activeGraph.getGraph();
    }

    public Map<Object, Point2D.Double> getActivePoints() {
        return activeGraph == null ? null : activeGraph.getPositionMap();
    }

    public <V> void setActivePoints(Map<V, Point2D.Double> pos) {
        if (activeGraph != null)
            activeGraph.setPositionMap(pos);
    }

    //
    // INPUT/OUTPUT
    //

    public void loadGraph(Graph sg, String name) {
        stopLayout();
        activeGraph.setGraph(sg);
        newPositions = null;
//        if (mainTM.getGraph() != sg)
//            mainTM.setGraph(sg);
        output("Successfully loaded graph " + name + " with " + sg.order() + " vertices and " + sg.edgeNumber() + " edges.");
    }

    public void loadLongitudinalGraph(LongitudinalGraph lg, String name) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void closeActiveGraph() {
        stopLayout();
        activeGraph.setGraph(null);
        mainTM.setController(null);
//        mainTM.setGraph(null);
    }

    //
    // LAYOUT METHODS
    //

    /** Layout engine that may animate */
    IterativeGraphLayout activeLayout;
    /** Timer handling animating layouts */
    BetterTimer layoutTimer;
    /** New positions for nodes when layout is reset */
    Map<Object,Point2D.Double> newPositions = null;

    static final int STATIC = 0, ANIMATING = 1;

    void setLayoutState(int state) {
        switch(state) {
            case STATIC: energyLayoutB.setSelected(false); break;
            case ANIMATING: energyLayoutB.setSelected(true); break;
        }
    }

    public <V> void initLayout(StaticGraphLayout layout, double... parameters) {
        stopLayout();
        activeLayout = null;
        newPositions = layout.layout(activeGraph.getGraph(), parameters);
        activeGraph.setPositionMap(newPositions);
        setLayoutState(STATIC);
    }

    public void initLayout(IterativeGraphLayout layout) {
        stopLayout();
        setLayoutState(STATIC);
        if (activeGraph() != null) {
            if (activeLayout == null || activeLayout != layout)
                activeLayout = layout;
            layout.reset(activeGraph.getPositionMap());
            newPositions = null;
        }
        if (activeLayout != null)
            propertyRP.add(layoutPS = new PropertySheet(activeLayout), "Energy layout parameters");
    }

    public void animateLayout() {
        layoutTimer = new BetterTimer(100);
        layoutTimer.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e) {
                iterateLayout();
            }
        });
        layoutTimer.start();
    }

    public void adjustLayout(StaticGraphLayout layout, double... parameters) {
        if (activeGraph != null && activeGraph() != null)
            newPositions = layout.layout(activeGraph(), parameters);
    }

    public void iterateLayout() {
        if (newPositions != null) {
            activeLayout.reset(newPositions);
            newPositions = null;
        }
        activeLayout.iterate(activeGraph.getGraph());
        activeGraph.setPositionMap(activeLayout.getPositions());
    }

    public void stopLayout() {
        if (layoutTimer != null)
            layoutTimer.stop();
        setLayoutState(STATIC);
    }

    //
    // HANDLE CHANGES TO GRAPH
    //

    public void stateChanged(ChangeEvent e) {
        if (activeGraph != null && e.getSource() == activeGraph) {
            newPositions = activeGraph.getPositionMap();
        }
    }

    public GraphController activeController() {
        throw new UnsupportedOperationException("Not supported yet.");
    }



}
